home *** CD-ROM | disk | FTP | other *** search
/ Experimental BBS Explossion 3 / Experimental BBS Explossion III.iso / gus / vts139b.zip / S3MLOADE.PAS < prev    next >
Pascal/Delphi Source File  |  1993-10-23  |  21KB  |  645 lines

  1. UNIT S3mLoader;
  2.  
  3. INTERFACE
  4.  
  5. USES Objects, SongUnit;
  6.  
  7.  
  8.  
  9.  
  10. PROCEDURE LoadS2mFileFormat  (VAR Song: TSong; VAR St: TStream; VAR Header: TSongHeader);
  11. PROCEDURE LoadS3mFileFormat  (VAR Song: TSong; VAR St: TStream; VAR Header: TSongHeader);
  12.  
  13.  
  14.  
  15.  
  16. IMPLEMENTATION
  17.  
  18. USES SongElements, SongUtils, Heaps, AsciiZ;
  19.  
  20.  
  21.  
  22.  
  23. {----------------------------------------------------------------------------}
  24. { Internal definitions. Format of the files.                                 }
  25. {____________________________________________________________________________}
  26.  
  27. TYPE
  28.   TS3mFileMagic1 = WORD;
  29.   TS3mFileMagic2 = ARRAY[0..3] OF CHAR;
  30.   TS2mFileMagic  = ARRAY[0..3] OF CHAR;
  31.  
  32. CONST
  33.   S3mMagic1 = $101A;
  34.   S3mMagic2 : TS3mFileMagic2 = ( 'S', 'C', 'R', 'M' );
  35.   S3mInstr2 : TS3mFileMagic2 = ( 'S', 'C', 'R', 'S' );
  36.   S2mMagic  : TS3mFileMagic2 = ( 'S', 'C', 'R', 'M' );
  37.  
  38. TYPE
  39.  
  40.   TS3mHeader =
  41.     RECORD
  42.       Name        : ARRAY[1..28] OF CHAR;
  43.       Magic1      : TS3mFileMagic1;
  44.       NPI1        : WORD;
  45.       SeqLen      : WORD;
  46.       NInstruments: WORD;
  47.       NPatts      : WORD;
  48.       Word4       : WORD;
  49.       Long1       : LONGINT;
  50.       Magic2      : TS3mFileMagic2;
  51.       Volume      : BYTE;
  52.       Tempo       : BYTE;
  53.       BPM         : BYTE;
  54.       fill1       : ARRAY[1..13] OF BYTE;
  55.       ChannelMaps : TPanPositions;
  56.     END;
  57.  
  58.   TS2mHeader =
  59.     RECORD
  60.       Name        : ARRAY[1..20] OF CHAR;
  61.       Scream      : ARRAY[1.. 8] OF CHAR;
  62.       Version     : BYTE;
  63.       fill1       : ARRAY[1.. 3] OF BYTE;
  64.       PattOfs     : WORD;
  65.       InstrOfs    : WORD;
  66.       SeqOfs      : WORD;
  67.       fill2       : ARRAY[1.. 4] OF BYTE;
  68.       Volume      : BYTE;
  69.       Tempo       : BYTE;
  70.       fill3       : ARRAY[1.. 4] OF BYTE;
  71.       NPatts      : WORD;
  72.       NInstruments: WORD;
  73.       SeqLen      : WORD;
  74.       Word4       : WORD;
  75.       Long1       : LONGINT;
  76.       Magic       : TS2mFileMagic;
  77.     END;
  78.  
  79.   TS3mInstrument =
  80.     RECORD
  81.       Flag      : BYTE;                 { 00 }
  82.       Name      : ARRAY[1..13] OF CHAR; { 01 }
  83.       Position  : WORD;                 { 0E }
  84.       Size      : LONGINT;              { 10 }
  85.       RepStart  : LONGINT;              { 14 }
  86.       RepLen    : LONGINT;              { 18 }
  87.       Volume    : WORD;                 { 1C }
  88.       Byte1     : BYTE;                 { 1E }
  89.       Looped    : BOOLEAN;              { 1F }
  90.       PeriodFine: WORD;                 { 20 }
  91.       fill3     : ARRAY[1..10] OF BYTE; { 22 }
  92.       Word3     : WORD;                 { 2C }
  93.       Word4     : WORD;                 { 2E }
  94.       Comment   : ARRAY[1..28] OF CHAR; { 30 }
  95.       Id        : TS3mFileMagic2;       { 4C }
  96.     END;
  97.  
  98.   TOffsets    = ARRAY[1..256] OF WORD;
  99.   TInstrFlags = ARRAY[1..256] OF BOOLEAN;
  100.  
  101. VAR
  102.   MaxChans   : WORD;
  103.   InitialPos : LONGINT;
  104.  
  105.  
  106.  
  107.  
  108. PROCEDURE SeekToOfs(VAR St: TStream; Ofs: WORD);
  109.   BEGIN
  110.     St.Seek(InitialPos + 16*LONGINT(Ofs));
  111.   END;
  112.  
  113.  
  114.  
  115. PROCEDURE ProcessPatterns(VAR Song: TSong; VAR St: TStream; VAR InstrFlags : TInstrFlags;
  116.                           VAR PattOfs: TOffsets; Num: WORD; S3m: BOOLEAN; Vers: BYTE);
  117.   VAR
  118.     Patt      : ARRAY[1..5000] OF BYTE;
  119.     FullTrack : TFullTrack;
  120.     Pattern   : PPattern;
  121.     Track     : PTrack;
  122.     Note      : TFullNote;
  123.     c         : BYTE;
  124.     i, j      : WORD;
  125.     n, t      : WORD;
  126.     Row       : WORD;
  127.     Size      : WORD;
  128.     NAdj      : WORD;
  129.     l         : LONGINT;
  130.     LastChan  : WORD;
  131.   LABEL
  132.     Ya, No;
  133.   BEGIN
  134.     t := 1;
  135.     FOR n := 1 TO Num DO
  136.       BEGIN
  137.         FOR i := 1 TO Song.SequenceLength DO
  138.           IF Song.PatternSequence^[i] = n THEN GOTO Ya;
  139.  
  140.         GOTO No;
  141. Ya:
  142. {WriteLn('Patt ', n : 3, FullHeap.HMaxAvail : 10, FullHeap.HMemAvail : 10);}
  143.         Pattern := Song.GetPattern(n);
  144.         IF Pattern = NIL THEN
  145.           BEGIN
  146.             Song.Status := msOutOfMemory;
  147.             EXIT;
  148.           END;
  149.  
  150.         WITH Pattern^.Patt^ DO
  151.           BEGIN
  152.             NNotes   := 64;
  153.             NChans   := Song.NumChannels;
  154.             Tempo    := 0;
  155.             BPM      := 0;
  156.           END;
  157.  
  158.         SeekToOfs(St, PattOfs[n]);
  159.  
  160.  
  161.         IF S3m OR (Vers > $1A) THEN
  162.           St.Read(Size, 2)
  163.         ELSE
  164.           Size := SizeOf(Patt) + 2;
  165.  
  166.         DEC(Size, 2);
  167.  
  168.         IF Size > SizeOf(Patt) THEN
  169.           Size := SizeOf(Patt);
  170.  
  171.         St.Read(Patt, Size);
  172.         IF St.Status <> stOk THEN
  173.           BEGIN
  174.             Song.Status := msFileTooShort;
  175.             EXIT;
  176.           END;
  177.  
  178.         LastChan := 1;
  179.         FOR j := 1 TO Song.NumChannels DO
  180.           BEGIN
  181.             FillChar(FullTrack, SizeOf(FullTrack), 0);
  182.  
  183.             i    := 1;
  184.             Row  := 0;
  185.             WHILE (i <= Size)         AND
  186.                   (S3m OR (Row < 64)) DO
  187.               BEGIN
  188.  
  189.                 c := Patt[i];
  190.                 INC(i);
  191.  
  192.                 IF c = 0 THEN
  193.                   Inc(Row)
  194.                 ELSE IF (c AND $1F) = (j - 1) THEN
  195.                   BEGIN
  196.  
  197.                     FillChar(Note, SizeOf(Note), 0);
  198.  
  199.                     IF c AND $20 <> 0 THEN
  200.                       BEGIN
  201.                         Note.Period     := Patt[i];
  202.                         IF NOT S3m THEN
  203.                           INC(Note.Period, $20);
  204. {                        INC(Note.Period, $10);}
  205.                         IF ((Note.Period AND $F0) > $90) OR
  206.                            ((Note.Period AND $F0) < $20) OR
  207.                            ((Note.Period AND $0F) > $0B) THEN
  208.                           BEGIN
  209. {
  210.                             Song.Status := msFileDamaged;
  211.                             EXIT;
  212. }
  213.                             Note.Period := 0;
  214.                           END;
  215.  
  216.                         IF Note.Period <> 0 THEN
  217.                           BEGIN
  218.                             Note.Period := PeriodSet[
  219.                               (Note.Period SHR 4) - 2, Note.Period AND 15];
  220.                             IF MaxChans <= (c AND $1F) THEN
  221.                               MaxChans := (c AND $1F) + 1;
  222.                           END;
  223.  
  224.                         Note.Instrument := Patt[i+1];
  225.  
  226.                         IF Note.Instrument <> 0 THEN
  227.                           InstrFlags[Note.Instrument] := TRUE;
  228.  
  229.                         INC(i, 2);
  230.                       END;
  231.  
  232.                     IF c AND $40 <> 0 THEN
  233.                       BEGIN
  234.                         Note.Volume := Patt[i] + 1;
  235.                         IF Note.Volume > 64 THEN
  236.                           Note.Volume := 64;
  237.                         INC(i, 1);
  238.                       END;
  239.  
  240.                     IF c AND $80 <> 0 THEN
  241.                       BEGIN
  242.                         Note.Parameter := Patt[i+1];
  243.                         CASE Patt[i] OF
  244.                            1 : BEGIN
  245.                                  Note.Command := mcSetTempo;
  246.                                  IF NOT S3m THEN
  247.                                    Note.Parameter := Note.Parameter SHR 4;
  248.                                END;
  249.                            2 : BEGIN
  250.                                  Note.Command := mcJumpPattern;
  251.                                  INC(Note.Parameter);
  252.                                END;
  253.                            3 : Note.Command := mcEndPattern;
  254.                            4 : BEGIN
  255.                                  IF Note.Parameter > $F0 THEN
  256.                                    BEGIN
  257.                                      Note.Command   := mcVolFineDown;
  258.                                      Note.Parameter := Note.Parameter AND $F;
  259.                                    END
  260.                                  ELSE IF ((Note.Parameter AND $F) = $F) AND
  261.                                          (Note.Parameter > $F)          THEN
  262.                                    BEGIN
  263.                                      Note.Command   := mcVolFineUp;
  264.                                      Note.Parameter := Note.Parameter SHR 4;
  265.                                    END
  266.                                  ELSE
  267.                                    Note.Command := mcVolSlide;
  268.                                END;
  269.                            5 : BEGIN
  270.                                  IF Note.Parameter >= $F0 THEN
  271.                                    BEGIN
  272.                                      Note.Command   := mcFinePortaDn;
  273.                                      Note.Parameter := Note.Parameter AND $F;
  274.                                    END
  275.                                  ELSE IF Note.Parameter >= $E0 THEN
  276.                                    BEGIN
  277.                                      Note.Command   := mcFinePortaDn;
  278.                                      Note.Parameter := ((Note.Parameter AND $F) + 2) SHR 2;
  279.                                    END
  280.                                  ELSE
  281.                                    Note.Command := mcTPortDown;
  282.                                END;
  283.                            6 : BEGIN
  284.                                  IF Note.Parameter >= $F0 THEN
  285.                                    BEGIN
  286.                                      Note.Command   := mcFinePortaUp;
  287.                                      Note.Parameter := Note.Parameter AND $F;
  288.                                    END
  289.                                  ELSE IF Note.Parameter >= $E0 THEN
  290.                                    BEGIN
  291.                                      Note.Command   := mcFinePortaUp;
  292.                                      Note.Parameter := ((Note.Parameter AND $F) + 2) SHR 2;
  293.                                    END
  294.                                  ELSE
  295.                                    Note.Command := mcTPortUp;
  296.                                END;
  297.                            7 : Note.Command := mcNPortamento;
  298.                            8 : Note.Command := mcVibrato;
  299.                           10 : Note.Command := mcArpeggio;
  300.                           11 : Note.Command := mcVib_VSlide;
  301.                           15 : Note.Command := mcSampleOffs;
  302.                           17 : BEGIN
  303.                                  Note.Command   := mcS3mRetrigNote;
  304.                                  Note.Parameter := Note.Parameter;
  305.                                END;
  306.                           20 : IF Note.Parameter > $30 THEN
  307.                                  Note.Command   := mcSetTempo
  308.                                ELSE
  309.                                  Note.Command   := mcNone;
  310.                         ELSE
  311.                           Note.Command := TModCommand(ORD(mcLast) + Patt[i]);
  312.                         END;
  313.  
  314.                         IF ((Note.Command = mcEndPattern) OR (Note.Command = mcJumpPattern)) AND
  315.                            (Pattern^.Patt^.NNotes > Row + 1) THEN
  316.                           Pattern^.Patt^.NNotes := Row + 1;
  317.  
  318.                         INC(i, 2);
  319.                       END;
  320.  
  321.                     FullTrack[Row] := Note;
  322.                   END
  323.                 ELSE
  324.                   BEGIN
  325.                     IF (j = 1) AND (LastChan < (c AND $1F) + 1) THEN
  326.                       LastChan := (c AND $1F) + 1;
  327.                     IF c AND $20 <> 0 THEN INC(i, 2);
  328.                     IF c AND $40 <> 0 THEN INC(i, 1);
  329.                     IF c AND $80 <> 0 THEN INC(i, 2);
  330.                   END;
  331.               END;
  332.  
  333.             Track := Song.GetTrack(t);
  334.             IF Track = NIL THEN
  335.               BEGIN
  336.                 Song.Status := msOutOfMemory;
  337.                 EXIT;
  338.               END;
  339.  
  340.             Track^.SetFullTrack(FullTrack);
  341.  
  342.             Pattern^.Patt^.Channels[j] := t;
  343.  
  344.             INC(t);
  345.  
  346.             IF j > LastChan THEN GOTO No;
  347.           END;
  348. No:
  349.       END;
  350.   END;
  351.  
  352.  
  353. PROCEDURE ProcessInstruments(VAR Song: TSong; VAR St: TStream; VAR InstrFlags : TInstrFlags;
  354.                              VAR InstrOfs: TOffsets; Num: WORD; S3m: BOOLEAN; Vers: BYTE);
  355.   VAR
  356.     Instrument : TInstrumentRec;
  357.     Instr      : PInstrument;
  358.     S3mInstr   : TS3mInstrument;
  359.     i, w       : WORD;
  360.     Signo      : LONGINT;
  361.     NoSigno    : LONGINT;
  362.   BEGIN
  363.     FOR i := 1 TO Num DO
  364.       WITH Instrument DO
  365.         BEGIN
  366. {WriteLn('Instr ', i : 3, FullHeap.HMaxAvail : 10, FullHeap.HMemAvail : 10);}
  367.           FillChar(Instrument, SizeOf(Instrument), 0);
  368.  
  369.           Instr := Song.GetInstrument(i);
  370.           IF Instr = NIL THEN
  371.             BEGIN
  372.               Song.Status := msOutOfMemory;
  373.               EXIT;
  374.             END;
  375.  
  376.           SeekToOfs(St, InstrOfs[i]);
  377.           St.Read(S3mInstr, SizeOf(S3mInstr));
  378.  
  379.           IF S3mInstr.Comment[1] <> #0 THEN
  380.             Instr^.SetName(StrASCIIZ(S3mInstr.Comment, 25))
  381.           ELSE
  382.             Instr^.SetName(StrASCIIZ(S3mInstr.Name,    13));
  383.  
  384.           IF S3mInstr.Flag = 1 THEN
  385.             BEGIN
  386.               IF InstrFlags[i] THEN
  387.                 Len := S3mInstr.Size;
  388.  
  389.               IF Len > 0 THEN
  390.                 BEGIN
  391.  
  392.                   IF S3mInstr.Looped THEN
  393.                     BEGIN
  394.                       Reps := S3mInstr.RepStart;
  395.                       Repl := S3mInstr.RepLen - Reps;
  396.                     END
  397.                   ELSE
  398.                     BEGIN
  399.                       Reps := 0;
  400.                       Repl := 0;
  401.                     END;
  402.  
  403.                   Vol  := S3mInstr.Volume;
  404.                   DAdj := S3mInstr.PeriodFine;
  405.                   IF S3m THEN
  406.                     NAdj := $20AB
  407.                   ELSE
  408.                     NAdj := $2100;
  409.  
  410.                   IF Repl        > Len THEN Repl := Len;
  411.                   IF Reps + Repl > Len THEN Repl := Len - Reps;
  412.  
  413.                   IF Vol > $40 THEN
  414.                     Vol := $40;
  415.  
  416.                   SeekToOfs(St, S3mInstr.Position);
  417.  
  418.                   IF Len <= MaxSample THEN
  419.                     BEGIN
  420.                       FullHeap.HGetMem(POINTER(Data), Len);
  421.                       IF Data = NIL THEN BEGIN
  422.                         Song.Status := msOutOfMemory;
  423.                         EXIT;
  424.                       END;
  425.  
  426.                       St.Read(Data^, Len);
  427.  
  428.                       IF St.Status <> stOk THEN BEGIN
  429.                         Song.Status := msFileDamaged;
  430.                         EXIT;
  431.                       END;
  432.  
  433.                       Signo   := 0;
  434.                       NoSigno := 0;
  435.                       FOR w := 1 TO Len - 1 DO
  436.                         BEGIN
  437.                           IF (Data^[w-1] XOR Data^[w]) AND $80 <> 0 THEN
  438.                             BEGIN
  439.                               IF (SHORTINT(Data^[w]   - 64) < 0) AND
  440.                                  (SHORTINT(Data^[w-1] - 64) < 0) THEN
  441.                                 INC(Signo)
  442.                               ELSE IF (SHORTINT(Data^[w]   - 64) >= 0) AND
  443.                                       (SHORTINT(Data^[w-1] - 64) >= 0) THEN
  444.                                 INC(NoSigno)
  445.                             END;
  446.                         END;
  447.  
  448.                       IF NoSigno > Signo THEN
  449.                         FOR w := 0 TO Len - 1 DO
  450.                           INC(Data^[w], 128);
  451.  
  452.                     END
  453.                   ELSE
  454.                     BEGIN
  455.                       FullHeap.HGetMem(POINTER(Data), MaxSample);
  456.                       FullHeap.HGetMem(POINTER(Xtra), Len-MaxSample);
  457.  
  458.                       IF (Data = NIL) OR (Xtra = NIL) THEN BEGIN
  459.                         Song.Status := msOutOfMemory;
  460.                         EXIT;
  461.                       END;
  462.  
  463.                       St.Read(Data^, MaxSample);
  464.                       St.Read(Xtra^, Len-MaxSample);
  465.  
  466.                       IF St.Status <> 0 THEN BEGIN
  467.                         Song.Status := msFileDamaged;
  468.                         EXIT;
  469.                       END;
  470.                     END;
  471.  
  472.                   Instr^.Change(@Instrument);
  473.                 END
  474.               ELSE
  475.                 Instr^.Change(NIL);
  476.             END;
  477.         END;
  478.   END;
  479.  
  480.  
  481. PROCEDURE LoadS3mFileFormat(VAR Song: TSong; VAR St: TStream; VAR Header: TSongHeader);
  482.   VAR
  483.     Hdr        : TS3mHeader ABSOLUTE Header;
  484.     InstrOfs   : TOffsets;
  485.     PattOfs    : TOffsets;
  486.     i          : WORD;
  487.     InstrFlags : TInstrFlags;
  488.   BEGIN
  489.     Song.FileFormat := mffS3m;
  490.  
  491.     InitialPos := St.GetPos;
  492.  
  493.     St.Seek(InitialPos + SizeOf(TS3mHeader));
  494.  
  495.     IF {(Hdr.Magic1 <> S3mMagic1) OR }(Hdr.Magic2 <> S3mMagic2) THEN
  496.       BEGIN
  497.         Song.Status := msNotLoaded;
  498.         EXIT;
  499.       END;
  500.  
  501.     Song.Status := msOK;
  502.  
  503.     FillChar(InstrFlags, SizeOf(InstrFlags), 0);
  504.  
  505.     Song.Name := FullHeap.HNewStr(StrASCIIZ(Hdr.Name, 28));
  506.  
  507.     IF Hdr.Volume = 64 THEN Hdr.Volume := 63;
  508. {    Song.FirstTick    := TRUE;}
  509.     Song.InitialTempo := Hdr.Tempo;
  510.     Song.InitialBPM   := Hdr.BPM;
  511.     Song.Volume       := Hdr.Volume * 4 + 3;
  512.     Song.NumChannels  := MaxChannels;
  513.     MaxChans := 1;
  514.  
  515.     FOR i := 1 TO 32 DO
  516.       IF Hdr.ChannelMaps[i] <> $FF THEN
  517.         IF (Hdr.ChannelMaps[i] AND 8) = 0 THEN
  518.           Song.PanPositions[i] := $40
  519.         ELSE
  520.           Song.PanPositions[i] := $B0;
  521.  
  522.     Song.SequenceRepStart := 0;{Hdr.NPI1 + 1;}
  523.     St.Read(Song.PatternSequence^, Hdr.SeqLen);
  524.  
  525.     IF Hdr.SeqLen > Song.SongLen THEN
  526.       Hdr.SeqLen := Song.SongLen;
  527.     Song.SequenceLength   := Hdr.SeqLen;
  528.  
  529.     FOR i := 1 TO Hdr.SeqLen DO
  530.       INC(Song.PatternSequence^[i]);
  531.  
  532.     St.Read(InstrOfs, Hdr.NInstruments*2);
  533.     St.Read(PattOfs,  Hdr.NPatts*2);
  534.  
  535.     WHILE (Song.SequenceLength                        > 1) AND
  536.           (Song.PatternSequence^[Song.SequenceLength] = 0) DO
  537.       DEC(Song.SequenceLength);
  538.  
  539.     FOR i := 1 TO Song.SongStart - 1 DO
  540.       Song.PatternSequence^[i] := 0;
  541.  
  542.  
  543.     { Processing of the patterns (the partiture) }
  544.  
  545.     ProcessPatterns(Song, St, InstrFlags, PattOfs, Hdr.NPatts, TRUE, $FF);
  546.     IF Song.Status > msOk THEN EXIT;
  547.  
  548.  
  549.     { Processing of the instruments }
  550.  
  551.     ProcessInstruments(Song, St, InstrFlags, InstrOfs, Hdr.NInstruments, TRUE, $FF);
  552.     IF Song.Status > msFileTooShort THEN EXIT;
  553.  
  554.     IF Song.NumChannels > MaxChans THEN
  555.       Song.NumChannels := MaxChans;
  556.   END;
  557.  
  558.  
  559.  
  560.  
  561. PROCEDURE LoadS2mFileFormat(VAR Song: TSong; VAR St: TStream; VAR Header: TSongHeader);
  562.   VAR
  563.     Hdr        : TS2mHeader ABSOLUTE Header;
  564.     InstrOfs   : TOffsets;
  565.     PattOfs    : TOffsets;
  566.     i          : WORD;
  567.     InstrFlags : TInstrFlags;
  568.   BEGIN
  569.     Song.FileFormat := mffS2m;
  570.  
  571.     InitialPos := St.GetPos;
  572.  
  573.     St.Seek(InitialPos + SizeOf(TS2mHeader));
  574.  
  575.     IF Hdr.Magic <> S2mMagic THEN
  576.       BEGIN
  577.         Song.Status := msNotLoaded;
  578.         EXIT;
  579.       END;
  580.  
  581.     Song.Status := msOK;
  582.  
  583.     FillChar(InstrFlags, SizeOf(InstrFlags), 0);
  584.  
  585.     Song.Name := FullHeap.HNewStr(StrASCIIZ(Hdr.Name, 20));
  586.  
  587.     IF Hdr.Volume = 64 THEN Hdr.Volume := 63;
  588.     Song.FirstTick    := TRUE;
  589.     Song.InitialTempo := Hdr.Tempo SHR 4;
  590.     Song.InitialBPM   := 125;
  591.     Song.Volume       := Hdr.Volume * 4 + 3;
  592.     Song.NumChannels  := MaxChannels;
  593.     MaxChans := 1;
  594.  
  595.     Song.SequenceRepStart := 0;
  596.  
  597.     SeekToOfs(St, Hdr.InstrOfs);
  598.     St.Read(InstrOfs, (Hdr.NInstruments*2 + 15) AND $FFF0);
  599.  
  600.     SeekToOfs(St, Hdr.PattOfs);
  601.     St.Read(PattOfs,  (Hdr.NPatts*2 + 15) AND $FFF0);
  602.  
  603.     SeekToOfs(St, Hdr.SeqOfs);
  604.     St.Read(Song.PatternSequence^, 16);
  605.     St.Read(Song.PatternSequence^, 16);
  606.  
  607.     DEC(Hdr.SeqLen);
  608.     FOR i := 1 TO Hdr.SeqLen DO
  609.       BEGIN
  610.         St.Read(Song.PatternSequence^[i], 5);
  611.         INC(Song.PatternSequence^[i]);
  612.       END;
  613.  
  614.     IF Hdr.SeqLen > Song.SongLen THEN
  615.       Hdr.SeqLen := Song.SongLen;
  616.     Song.SequenceLength   := Hdr.SeqLen;
  617.  
  618.     WHILE (Song.SequenceLength                        > 1) AND
  619.           (Song.PatternSequence^[Song.SequenceLength] = 0) DO
  620.       DEC(Song.SequenceLength);
  621.  
  622.     FOR i := 1 TO Song.SongStart - 1 DO
  623.       Song.PatternSequence^[i] := 0;
  624.  
  625.  
  626.     { Processing of the patterns (the partiture) }
  627.  
  628.     ProcessPatterns(Song, St, InstrFlags, PattOfs, Hdr.NPatts, FALSE, Hdr.Version);
  629.     IF Song.Status > msOk THEN EXIT;
  630.  
  631.  
  632.     { Processing of the instruments }
  633.  
  634.     ProcessInstruments(Song, St, InstrFlags, InstrOfs, Hdr.NInstruments, FALSE, Hdr.Version);
  635.     IF Song.Status > msFileTooShort THEN EXIT;
  636.  
  637.     IF Song.NumChannels > MaxChans THEN
  638.       Song.NumChannels := MaxChans;
  639.   END;
  640.  
  641.  
  642.  
  643.  
  644. END.
  645.